package proto

import (
	"bytes"
	"errors"
	"fmt"
	"math"
)

// MsgStruct 消息结构体
// lenHeader lenBody Header Body
// len均为32位长度 4bytes
type MsgStruct struct {
	Header    []byte
	Body      []byte
	lenHeader int
	lenBody   int
}

func NewMsgStructFromBytes(raw []byte) *MsgStruct {
	msgStruct := MsgStruct{}
	err := msgStruct.FromBytes(raw)
	if nil != err {
		return nil
	} else {
		return &msgStruct
	}
}

func NewMsgStructFromParam(header string, body string) *MsgStruct {
	lenHeader := len(header)
	lenBody := len(body)
	if lenHeader > math.MaxInt32 || lenBody > math.MaxInt32 {
		// 字节超长
		return nil
	}
	return &MsgStruct{
		Header:    []byte(header),
		Body:      []byte(body),
		lenHeader: lenHeader,
		lenBody:   len(body),
	}
}

func (m *MsgStruct) ToBytes() []byte {
	m.lenHeader = len(m.Header)
	m.lenBody = len(m.Body)
	buffer := bytes.Buffer{}
	buffer.Write(IntToBytes(m.lenHeader))
	buffer.Write(IntToBytes(m.lenBody))
	buffer.Write(m.Header)
	buffer.Write(m.Body)
	return buffer.Bytes()
}

func (m *MsgStruct) FromBytes(raw []byte) error {
	lenRaw := len(raw)
	if lenRaw < 8 {
		// msg至少8位
		return errors.New("too short")
	}
	m.lenHeader = BytesToInt(raw[:4])
	m.lenBody = BytesToInt(raw[4:8])
	lenCalc := 8 + m.lenHeader + m.lenBody
	if lenRaw != lenCalc {
		return errors.New(fmt.Sprintf("len not match raw %d != %d calc", lenRaw, lenCalc))
	}
	m.Header = raw[8 : 8+m.lenHeader]
	m.Body = raw[8+m.lenHeader : lenCalc]
	return nil
}

func IntToBytes(raw int) []byte {
	result := make([]byte, 4)
	result[0] = byte((raw >> 24) & 0xff)
	result[1] = byte((raw >> 16) & 0xff)
	result[2] = byte((raw >> 8) & 0xff)
	result[3] = byte(raw & 0xff)
	return result
}

func BytesToInt(raw []byte) int {
	if 4 != len(raw) {
		return 0
	}
	return int(raw[0]&0xff)<<24 |
		int(raw[1]&0xff)<<16 |
		int(raw[2]&0xff)<<8 |
		int(raw[3]&0xff)
}

func (m *MsgStruct) GetHeader() string {
	return string(m.Header)
}

func (m *MsgStruct) GetBody() string {
	return string(m.Body)
}
